﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication1
{
	class BackupFiles
	{
		private Control syncControl;
		private bool isBusy;
		private string backupPath;
		private string destinationPath;
		private string jobName;
		private DateTime startTime;
		private bool canceled;
		private bool error;
		private bool onlyNewerFiles;
		private DateTime lastUpdate;
		private int updateInterval;

		public bool Error
		{
			get { return error; }
		}

		public bool IsBusy
		{
			get { return isBusy; }
		}

		public BackupFiles(Control syncControl, int updateInterval)
		{
			this.syncControl = syncControl;
			isBusy = false;
			lastUpdate = DateTime.Now;
			this.updateInterval = updateInterval;
		}

		public void Backup(string backupPath, string destinationPath, string jobName, bool onlyNewerFiles)
		{
			if (!isBusy)
			{
				isBusy = true;
				this.backupPath = backupPath;
				this.destinationPath = destinationPath;
				this.jobName = jobName;
				this.onlyNewerFiles = onlyNewerFiles;
				error = false;
				canceled = false;
				startTime = DateTime.Now;
				Thread t = new Thread(new ThreadStart(StartBackup));
				t.IsBackground = true;
				t.Name = string.Format("Backup job '{0}'", jobName);
				t.Start();
			}
		}

		private void StartBackup()
		{
			int fileCounter = 0;
			long bytesProcessed = 0;
			try
			{
				Directory.CreateDirectory(destinationPath);
				DirectoryInfo di = new DirectoryInfo(backupPath);
				BackupRecursive(di, ref fileCounter, ref bytesProcessed);
			}
			catch (Exception ex)
			{
				DoErrorOccurred(ex, "", ErrorType.Unknown);
			}
			DoBackupComplete(fileCounter, bytesProcessed);
		}

		private void BackupRecursive(DirectoryInfo directoryInfo, ref int fileCounter, ref long bytesProcessed)
		{
			// DestinationPathString
			string destinationPathString = destinationPath + directoryInfo.FullName.Substring(backupPath.Length) + @"\";


			// Backup files
			foreach (FileInfo file in directoryInfo.GetFiles())
			{
				string destName = "";
				try
				{
					if (canceled) break;

					destName = destinationPathString + file.Name;
					fileCounter++;
					bytesProcessed += file.Length;
					DoProgress(file.FullName, fileCounter, bytesProcessed);

					if (onlyNewerFiles)
					{
						bool doCopy = false;
						
						if (File.Exists(destName))
						{
							FileInfo tFile = new FileInfo(destName);
							if (file.LastWriteTimeUtc > tFile.LastWriteTimeUtc) doCopy = true;
						}
						else doCopy = true;

						
						if (doCopy) file.CopyTo(destName, true);
					}
					else
					{
						file.CopyTo(destName, true);
					}
				}
				catch (Exception ex)
				{
					DoErrorOccurred(ex, destName, ErrorType.FileCopyError);
				}
			}

			// Make subdirs and do a recusive call for each subdir
			foreach (DirectoryInfo subDir in directoryInfo.GetDirectories())
			{
				string createDir = "";
				try
				{
					if (canceled) break;
					createDir = destinationPathString + subDir.Name + @"\";
					Directory.CreateDirectory(createDir);
					BackupRecursive(subDir, ref fileCounter, ref bytesProcessed);
				}
				catch (Exception ex)
				{
					DoErrorOccurred(ex, createDir, ErrorType.DirectoryCreateError);
				}
			}
		}

		public void Cancel()
		{
			canceled = true;
		}

		public event EventHandler<BackupEventArgs> Progress;
		public event EventHandler<CompleteEventArgs> BackupComplete;
		public event EventHandler<ErrorEventArgs> ErrorOccurred;

		private void DoErrorOccurred(Exception ex, string currentPath, ErrorType errorType)
		{
			error = true;
			if (ErrorOccurred != null)
			{
				if (syncControl == null) ErrorOccurred(this, new ErrorEventArgs(ex, errorType, currentPath, jobName));
				else syncControl.BeginInvoke(ErrorOccurred, this, new ErrorEventArgs(ex, errorType, currentPath, jobName));
			}
		}

		private void DoProgress(string message, int filesProcessed, long bytesProcessed)
		{
			if (Progress != null)
			{
				if ((DateTime.Now - lastUpdate).TotalMilliseconds > updateInterval)
				{
					lastUpdate = DateTime.Now;
					if (syncControl == null) Progress(this, new BackupEventArgs(message, DateTime.Now - startTime, filesProcessed, bytesProcessed));
					else syncControl.BeginInvoke(Progress, this, new BackupEventArgs(message, DateTime.Now - startTime, filesProcessed, bytesProcessed));
				}
			}
		}

		private void DoBackupComplete(int filesProcessed, long bytesProcessed)
		{
			isBusy = false;
			if (BackupComplete != null)
			{
				if (syncControl == null) BackupComplete(this, new CompleteEventArgs(DateTime.Now - startTime, filesProcessed, bytesProcessed));
				else syncControl.BeginInvoke(BackupComplete, this, new CompleteEventArgs(DateTime.Now - startTime, filesProcessed, bytesProcessed)); 
			}
		}

		private class WorkerCopyInfo
		{
			public FileInfo File { get; set; }
			public string Destination { get; set; }

			public WorkerCopyInfo(FileInfo file, string destination)
			{
				File = file;
				Destination = destination;
			}
		}
	}
}
